# TriggerSwitchD, executes commands to activate/deactive remote switches.
# (c) Copyright 2010 Pontus Strömdahl, AdhocSkill. 
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


require File.join(File.dirname(__FILE__),"trigger_switch_d_helper")

module TriggerSwitchD
  #Uses the ruby engine to transform code_line into method execute on an instance of Action
  class Action

    #Takes a code_line to be scheduled or executed together with the devices, environment
    #and date to validate it.
    def initialize(code_line,devices,environment=Config.default_environment,date=Date.today)
      @name = code_line.strip
      @date = date
      @at_location = environment[:at_location]
      @lookup = environment[:lookup]
      tokens = code_line.scan(/(\w+) (\w+) (\w+) ([\w\d:]+)/u).flatten
      ghost = (class << self;self;end)
      ghost.methodize_device(tokens[1],devices,@lookup)
      @at, executable = __build_executable_at__(tokens)
      ghost.methodize_executable(executable)
    end
    
    def activate(*args) #:nodoc:
      Action.action(:turn_on,*args)
    end
    
    def deactivate(*args) #:nodoc:
      Action.action(:turn_off,*args)
    end
    
    def at(*args) #:nodoc:
      time = *args
      return @at if time == nil
      if (time =~ /(?:2[0-3]|[01]\d):[0-5]\d/) == 0
        return time
      end
      at_calculated_time(*args)
    end
    
    def to_s
      @name
    end
    
    private
    
    def __build_executable_at__(tokens)
      code = __translate__(tokens).reverse
      code[1..3].inject(code[0]) do |part,val|
        send(val, *part)
      end
    end
    
    def __translate__(tokens)
      tokens.map do |token|
        (@lookup.include? token) ? @lookup[token] : token
      end
    end
    
    def at_calculated_time(*args) #:nodoc:
      method_name = *args
      @date.extend(Daylight)
      time = @date.send(method_name,@at_location)
      time.localtime.strftime("%H:%M")
    end

    def self.action(*args)
      event, switches, at_time = *args
      call_args = Action.__build_action_args__(event,switches)
      code = Proc.new do |output|
        Action.__action_gut__(output,*call_args)
      end
      [at_time,code]
    end
    
    def self.__build_action_args__(event,switches)
      switches.map do |switch|
        switch_array = switch.to_a_by_keys(%w{model_name house_code unit_code})
        switch_array.insert(1,event)
        switch_array
      end
    end
    
    def self.__action_gut__(*args)
      output, model_name, *params = args.flatten
      Tellstick::create(output,model_name).send(*params)
    end
    
    def self.methodize_executable(executable)
      define_method(:execute,&executable)
    end
    
    def self.methodize_device(name,devices,lookup)
      return unless devices.include? name
      lookup[name] = :remote_device.to_s
      define_method(:remote_device) do |*args|
        time = *args
        [devices[name],time]
      end
    end
  end
end